package com.kaigejava.aspect;


import cn.hutool.json.JSONUtil;
import com.kaigejava.aspect.annotates.LogAspectAnnotate;
import com.kaigejava.pojo.SysLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author 凯哥Java
 * @description 记录业务日志的aspect对象
 * @company 凯哥Java(http : / / www.kaigejava.com)
 * @since 2023/12/16 23:18
 */
@Aspect
@Component
@Slf4j
public class LogAspect {
    private String requestPath = null; // 请求地址
    private long startTimeMillis = 0; // 开始时间
    private long endTimeMillis = 0; // 结束时间
    private String user = null; // 操作人
    private HttpServletRequest request = null;//请求

    /**
     * 注解的位置
     */
    @Pointcut("@annotation(com.kaigejava.aspect.annotates.LogAspectAnnotate)")
    public void logPointCut() {
    }


    /**
     * 需要扫描的包及其子包
     */
    @Pointcut("execution(public * com.kaigejava.controller..*.*(..))")
    public void pointcutController() {}


    /**
     * 使用逻辑OR组合两个切入点
     */
    @Pointcut("logPointCut() || pointcutController()")
    public void combinedPointcut() {
    }

    /**
     * @param joinPoint
     * @Description 前置通知  方法调用前触发   记录开始时间,从session中获取操作人
     */
    //这个只是基于注解的
    //@Before(value = "logPointCut()")
    //这个是基于注解和指定的包的
    @Before(value = "combinedPointcut()")
    public void before(JoinPoint joinPoint) {
        startTimeMillis = System.currentTimeMillis();
        //获取目标方法
        String methodNam = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        //获取方法参数
        String params = Arrays.toString(joinPoint.getArgs());
        log.info("get in {} params :{}",methodNam,params);


    }

    /**
     * @param joinPoint
     * @return
     * @Description 获取入参方法参数
     */
    public Map<String, Object> getNameAndValue(JoinPoint joinPoint) {
        Map<String, Object> param = new HashMap<>();
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < paramNames.length; i++) {
            if (paramValues[i] instanceof Integer || paramValues[i] instanceof String) {
                param.put(paramNames[i], paramValues[i]);
            }
        }
        return param;
    }

    /**
     * @param joinPoint
     * @Description 后置通知    方法调用后触发   记录结束时间 ,操作人 ,入参等
     */
    //这个只是基于注解的
    //@After(value = "logPointCut()")
    //这个是基于注解和指定的包的
    @After(value = "combinedPointcut()")
    public void after(JoinPoint joinPoint) {
        request = getHttpServletRequest();
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class<?> targetClass = null;
        try {
            targetClass = Class.forName(targetName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Method[] methods = targetClass.getMethods();
        String title;
        String action;
        Integer sysType;
        Integer opType;
        Class<?>[] clazzs;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                clazzs = method.getParameterTypes();
                if (clazzs != null && clazzs.length == arguments.length) {
                    if(method.getAnnotation(LogAspectAnnotate.class) != null){
                        request = getHttpServletRequest();
                        requestPath = request.getServletPath();
                        HttpSession session = request.getSession();
                        //user = session.getAttribute("userName").toString();
                        //模拟当前登录用户
                        user = UUID.randomUUID().toString().replace("-","");
                        title = method.getAnnotation(LogAspectAnnotate.class).content();
                        action = method.getAnnotation(LogAspectAnnotate.class).action();
                        sysType = method.getAnnotation(LogAspectAnnotate.class).sysType();
                        opType = method.getAnnotation(LogAspectAnnotate.class).opType();
                        endTimeMillis = System.currentTimeMillis();

                        SysLog sysLog = new SysLog(user, requestPath,
                                (endTimeMillis - startTimeMillis) + "ms",
                                getNameAndValue(joinPoint).toString(), title, action, sysType, opType);
                        log.info("入库对象是:{}" , JSONUtil.toJsonStr(sysLog));
                        //TODO after之后，可以将日志入库的
//                    break;
                    }else if( method.getDeclaringClass().getName().startsWith("com.kaigejava.controller")){
                        log.info("访问的是指定包及其包下的");
                    }

                }else if(Objects.isNull(clazzs)){
                    System.out.printf("入参为null");
                }
            }
        }
    }

    /**
     * @Description: 获取request
     */
    public HttpServletRequest getHttpServletRequest() {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        return request;
    }

    /**
     * @param joinPoint
     * @return 环绕通知
     * @throws Throwable
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        return null;
    }

    /**
     * @param joinPoint
     * @Description 异常通知
     */
    @AfterThrowing(value = "combinedPointcut()")
    public void throwing(JoinPoint joinPoint) {
        System.out.println("哎呀，这里是异常处理的：异常通知");
    }

}
